trigger CampaignMember on CampaignMember (before update, before insert, after insert, after update)
{	Validation.setOppTriggerFired();
	
	// Retrieve Training and Event Booking PricebookEntry for reference
	PriceBookEntry trainingPricebookEntry =[SELECT id, name FROM PricebookEntry WHERE Name='Training and Event Booking' LIMIT 1][0];
	
	// Retrieve Opportunity Record Types for reference
	List<RecordType> recordTypes1 = [SELECT Name, Id FROM RecordType WHERE sObjectType='Opportunity' AND isActive=true];
	Map<String,String> opportunityRecordTypes = new Map <String,String>();
	for(RecordType recordType: recordTypes1)
	{	opportunityRecordTypes.put(recordType.Name, recordType.Id);
	}
	
	// Retrieve Campaign Record Types for reference
	List <RecordType> recordTypes2 = [SELECT Name, Id FROM RecordType WHERE sObjectType='Campaign' AND isActive=true];
	Map <Id, String> campaignRecordTypes = new Map<ID,String>();
	for(RecordType recordType :recordTypes2)
	{	campaignRecordTypes.put(recordType.Id, recordType.Name);  
	} 
	
	
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	
	// Define the delegate fee and update campaign member field
	 
	// List to store campaign ids
	List <Id> campaignIds = new List <Id>();
	
	// B - Add campaign member to list - cycle through new CMs, and add associated CAMPAIGN ID (campaignMember.CampaignId)
	for(CampaignMember campaignMember : trigger.new)
	{	campaignIds.add(campaignMember.CampaignId);
	}
	
	// B - Get Campaign record details from the campaign contained in the list (WHERE Id IN :campaignIds) 
	Map<Id,Campaign> targetedCampaigns = new Map<Id,Campaign>([SELECT Id, Name, Event_Organiser_Cost__c, Subscriber_Cost__c, Public_Cost__c, Program_School_Cost__c  FROM Campaign WHERE Id IN :campaignIds]);
	

	//B - Declare delegateFee variable
	Double delegateFee;
	
	List <CampaignMember> CMDelegateFeeRefUpdate = new List <CampaignMember>();
	List <CampaignMember> campaignMembersList = new List <CampaignMember>();
	for(CampaignMember campaignMember : trigger.new)
	{	campaignMembersList.add(campaignMember);
	}
	
	if(trigger.isBefore)
	{
		for(CampaignMember campaignMember : campaignMembersList)
		{											
				// B - Get record details from the map of the CMs associated campaign - 'get target campaign details where = campaignMember.CampaignId'     				
				Campaign campaign = targetedCampaigns.get(campaignMember.CampaignId);
									 
				// B - Map to store 'status' picklist values and associated 'Delegate Fee Paid'  
				Map <String, Double> statusToValue = new Map <String, Double>();
				
				statusToValue.put('Waiting List', NULL);
				statusToValue.put('Invited', NULL);
				statusToValue.put('Declined', 0.00); 
				statusToValue.put('Cancelled - refund', 0.00);
				statusToValue.put('Cancelled - substituted', 0.00);
				
				// B - check to see if statusToValue map contains status value of member
				if (statusToValue.containsKey(campaignMember.Status))
				{	// B - If yes, assign 'Delegate Fee Paid' value
					delegateFee = statusToValue.get(campaignMember.Status);
				}
				else
				{	Map <String, Double> statusToValueCheck2 = new Map <String, Double>();
					statusToValueCheck2.put('', campaign.Public_Cost__c);
					//statusToValueCheck2.put('Invited', campaign.Public_Cost__c);
					statusToValueCheck2.put('Subscriber', campaign.Subscriber_Cost__c);
					// Additional Values
					statusToValueCheck2.put('Foundation', campaign.Program_School_Cost__c);
					statusToValueCheck2.put('GFYL 1', campaign.Program_School_Cost__c);
					statusToValueCheck2.put('GFYL 2', campaign.Program_School_Cost__c);
					statusToValueCheck2.put('VP 3', campaign.Program_School_Cost__c);
					statusToValueCheck2.put('National 1', campaign.Program_School_Cost__c);
					statusToValueCheck2.put('National 2', campaign.Program_School_Cost__c);
					statusToValueCheck2.put('National 3', campaign.Program_School_Cost__c);
					statusToValueCheck2.put('National 4', campaign.Program_School_Cost__c);
					statusToValueCheck2.put('PPP', campaign.Program_School_Cost__c);
					statusToValueCheck2.put('QP 1', campaign.Program_School_Cost__c);
					
					if (campaignMember.Website_Fee__c != 0 && campaignMember.Website_Fee__c != 0.00 && campaignMember.Website_Fee__c != NULL)
					{	delegateFee = campaignMember.Website_Fee__c;
					}
					else if (campaignMember.Type__c == 'Promo')  
					{	delegateFee = NULL;
					}
					else if (statusToValueCheck2.containsKey(campaignMember.Funding_Type__c))
					{	delegateFee = statusToValueCheck2.get(campaignMember.Funding_Type__c);
					}
					else 
					{	delegateFee = campaign.Public_Cost__c; 
					}
				}
				campaignMember.Delegate_Fee_Paid_Ref__c = Double.valueOf(delegateFee);
				//CMDelegateFeeRefUpdate.add(campaignMember);
		} 
	}
	//update CMDelegateFeeRefUpdate;
	
	if(trigger.isAfter)
	{
		// Go through each Campaign Member populate OppIdTarget and opportunitiesToInsert
		List <Id> targetedOpportunityIds = new List <Id>();
		List <Opportunity> opportunitiesToInsert = new List <Opportunity>();
		for(CampaignMember campaignMember : Trigger.new)
		{	if(campaignMember.Income_Item__c != null)
			{	targetedOpportunityIds.add(campaignMember.Income_Item__c);
			}
			else 
			{	if(campaignRecordTypes.get(campaignMember.CampaignType__c) == 'SAKGF Training' && campaignMember.Delegate_Fee_Paid_Ref__c != null && campaignMember.Delegate_Fee_Paid_Ref__c != 0.00 && campaignMember.Delegate_Fee_Paid_Ref__c != 0)
				{	Opportunity opportunity = new Opportunity();
					opportunity.StageName = 'To Invoice';
					opportunity.CloseDate = System.today();  
					opportunity.AccountId = campaignMember.AccountId__c;
					opportunity.Name = 'Manual Booking - ' + campaignMember.ContactName__c;
					opportunity.CampaignId = campaignMember.CampaignId;
					opportunity.Related_Contact__c = campaignMember.ContactId;
					opportunity.Income_Type__c = 'Event/Training Booking';
					opportunity.RecordTypeId = opportunityRecordTypes.get('Donations & Purchases');
					opportunitiesToInsert.add(opportunity);
				} 
			}
		}
		
		// Insert Opportunities, Insert OpportunityContactRoles, populate Linked Contacts
		Map <Id, Id> contactId2OpportunityIdMap = new Map <Id, Id>();
		List <OpportunityContactRole> opportunityContactRoles = new List <OpportunityContactRole>();
		
		if(!opportunitiesToInsert.isEmpty())
		{	insert opportunitiesToInsert;
			for(Opportunity opportunity : opportunitiesToInsert)
			{	if (opportunity.Related_Contact__c != null)
				{	OpportunityContactRole opportunityContactRole = new opportunityContactRole();
					opportunityContactRole.IsPrimary = true;
					opportunityContactRole.OpportunityId = opportunity.Id;
					opportunityContactRole.ContactId = opportunity.Related_Contact__c;
					opportunityContactRole.Role = 'Purchaser';
					opportunityContactRoles.add(opportunityContactRole);
					contactId2OpportunityIdMap.put(opportunity.Related_Contact__c, opportunity.Id);
				}
				targetedOpportunityIds.add(opportunity.Id);
			}
			if(!opportunityContactRoles.isEmpty()) 
			{	insert opportunityContactRoles;
			}
		}
		
		// Insert "Linked" Campaign Members
		List <CampaignMember> linkedCampaignMembers = new List <CampaignMember>();
		if(!contactId2OpportunityIdMap.isEmpty())
		{	for(CampaignMember campaignMember : trigger.new)
			{	if(contactId2OpportunityIdMap.containsKey(campaignMember.ContactId))
				{	CampaignMember linkedCampaignMember = campaignMember.clone(true, true);
					linkedCampaignMember.Income_Item__c = contactId2OpportunityIdMap.get(campaignMember.ContactId);
					linkedCampaignMembers.add(linkedCampaignMember);
				}
			}
			update linkedCampaignMembers;
		}
		
		// Handle "Targeted" Opportunity
		List <Opportunity> targetedOpportunities = new List <Opportunity>();
		List <OpportunityLineItem> opportunityLineItemsToInsert = new List <OpportunityLineItem>();
		List <OpportunityLineItem> opportunityLineItemsToUpdate = new List <OpportunityLineItem>();
		
		
	
	
		if(!targetedOpportunityIds.isEmpty())
		{	targetedOpportunities = [SELECT Id, Name, (SELECT Id, Participant__c, Contact__c, Campaign_Member_Id__c, Member_Status__c, TotalPrice FROM OpportunityLineItems WHERE Campaign_Member_Id__c != '' AND TotalPrice >= 0) FROM Opportunity WHERE Id IN :targetedOpportunityIds];
			for(CampaignMember campaignMember : trigger.new)			
			{	if(campaignMember.Income_Item__c != null)
				{	Boolean opportunityLineItemExists = false;
					for(Opportunity opportunity : targetedOpportunities)
					{	if(opportunity.Id == campaignMember.Income_Item__c)
						{	for(OpportunityLineItem opportunityLineItem : opportunity.OpportunityLineItems)
							{	if(opportunityLineItem.Campaign_Member_ID__c == campaignMember.Id)
								{	opportunityLineItemExists = true;
									if (campaignMember.Status != opportunityLineItem.Member_Status__c)
									{	// Campaign Member Status has changed
										if (campaignMember.Status == 'Cancelled - refund' || campaignMember.Status == 'Cancelled - substituted')
										{	// Create Refund/Substituted OpportunityLineItem
											OpportunityLineItem newOpportunityLineItem = new OpportunityLineItem();
											newOpportunityLineItem.OpportunityId = campaignMember.Income_Item__c;
											newOpportunityLineItem.Campaign_Member_Id__c = campaignMember.Id;
											newOpportunityLineItem.Campaign__c = campaignMember.CampaignId;
											newOpportunityLineItem.Member_Status__c = campaignMember.Status;
											newOpportunityLineItem.PricebookEntryId = trainingPricebookEntry.Id;
											newOpportunityLineItem.Contact__c = campaignMember.ContactId;
											newOpportunityLineItem.Quantity = 1;
											newOpportunityLineItem.TotalPrice = -opportunityLineItem.TotalPrice;
											opportunityLineItemsToInsert.add(newOpportunityLineItem);
										}
										else
										{	// Update the existing OpportunityLineItem
											opportunityLineItem.Member_Status__c = campaignMember.Status;
											opportunityLineItem.Campaign__c = campaignMember.CampaignId;
											opportunityLineItem.Campaign_Member_ID__c = campaignMember.Id;
											if (campaignMember.Delegate_Fee_Paid_Ref__c == null)
											{	opportunityLineItem.TotalPrice = 0.00;
											}
											else
											{	opportunityLineItem.TotalPrice = campaignMember.Delegate_Fee_Paid_Ref__c; 
											}
											opportunityLineItemsToUpdate.add(opportunityLineItem);
										}
									}
								}
							}
							// Set Targeted Opportunity to "Opportunity Complete" for Manual Bookings
							if (opportunity.Name.startsWith('Manual Booking - '))
							{	opportunity.Opportunity_Complete__c = true;
							}
						}
					}
					//
					
					// confirm: what to do when the delegate fee is null...
					if(!opportunityLineItemExists && campaignMember.Delegate_Fee_Paid_Ref__c != null)
					{	// Create a new OpportunityLineItem
						OpportunityLineItem newOpportunityLineItem = new OpportunityLineItem();
						newOpportunityLineItem.OpportunityId = campaignMember.Income_Item__c;
						newOpportunityLineItem.Campaign_Member_Id__c = campaignMember.Id;
						newOpportunityLineItem.Campaign__c = campaignMember.CampaignId;
						newOpportunityLineItem.Member_Status__c = campaignMember.Status;
						newOpportunityLineItem.PricebookEntryId = trainingPricebookEntry.Id;
						newOpportunityLineItem.Contact__c = campaignMember.ContactId;
						newOpportunityLineItem.Quantity = 1;
						
						newOpportunityLineItem.TotalPrice = campaignMember.Delegate_Fee_Paid_Ref__c;
						opportunityLineItemsToInsert.add(newOpportunityLineItem);
					}
				}
			}
		}
		// Insert any new OpportunityLineItems}
		if(!opportunityLineItemsToInsert.isEmpty())
		{	insert opportunityLineItemsToInsert;
		}
		
		// Update the existing OpportunityLineItems
		if(!opportunityLineItemsToUpdate.isEmpty())
		{	update opportunityLineItemsToUpdate;
		}
		
		// Update the existing targetedOpportunities
		if(!targetedOpportunities.isEmpty())
		{	update targetedOpportunities;
		}
	}
}